﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace SpectationClient.SpectralTools {
    public class ASD_Writer {
        public ASD_Writer() { }

        public void createASDFiles(String path_dir, String file_prefix, List<Object> idList, List<Spectrum> spectra) {
            if (idList.Count != spectra.Count) throw new Exception("Class: ASD_Writer\nMethode: createASDFiles\nError: Number of spectraIDs is different of number of spectra!");
            if (!Directory.Exists(path_dir)) Directory.CreateDirectory(path_dir);
            for (int i = 0; i < spectra.Count; i++) {
                String fname = String.Format("{0}\\{1}_{2}.asd", path_dir, file_prefix, idList[i]);
                createASDFile(fname, spectra[i]);
            }
        }

        /// <summary>
        /// Write a single SPECLIB_Spectrum as ASD File.
        /// </summary>
        /// <param name="filename"></param>
        /// <param name="spectrum"></param>
        public void createASDFile(String filename, Spectrum spectrum) {
            byte[] bytes = getASDBytes(spectrum);
            FileStream fs = new FileStream(filename, FileMode.Create, FileAccess.Write);
            fs.Write(bytes, 0, bytes.Length);
            fs.Close();
        }

        /// <summary>
        /// Returns a SPECLIB_Spectrum as ASD FieldSpec Format Byte Array.
        /// </summary>
        /// <param name="spectrum"></param>
        /// <returns></returns>
        public Byte[] getASDBytes(Spectrum spectrum) {
            if(spectrum.originalASD_BLOB != null) return spectrum.originalASD_BLOB;

            int head = 484; //header offset
            int tail;
            Type dataType = spectrum.DataType;
            tail = spectrum.NumberOfBands * sizeof(Double);

            int size = -1;

           
            if(dataType == typeof(Int32)) {
                size = sizeof(Int32);
            } else if(dataType == typeof(Single)) {
                size = sizeof(Single);
            } else {
                size = sizeof(Double); 
            }
            tail = spectrum.NumberOfBands * size;
            int totalLength = head + tail;
            Byte[] ba = new Byte[totalLength];
            for(int i = 0; i < ba.LongLength; i++) ba[i] = 0;
            BinaryBuilder B = BinaryBuilder.CreateBinaryBuilder(ba);
            //Set header infos
            B.Write( 0, "ASD");
            B.Write( 3, "SpectationClient");
            //TODO ByteArrayConverter.From(spectrum.
            //160	18	struct tm 	when;
            //178	1	byte	program_version;
            //179	1	byte	file_version;
            //180	1	byte	itime;
            //181	1	byte	dc_corr;
            //182	4	time_t (==long)	dc_time;
            //ByteArrayConverter.
            B.Write( 186, getFlagSpectrumType(spectrum.SpecType));//means Ref, Raw, etc.
            //TODO
            //187	4	time_t (==long)	ref_time;
            B.Write(191, (float) spectrum.WL_Min);  //191	4	float	ch1_wavel;
            float wavel_step = (float) Math.Round(spectrum.Bands[1] - spectrum.Bands[0]);
            if(wavel_step == 0.0) wavel_step = 1;
            B.Write(195, wavel_step);  //195	4	float	wavel_step;
            B.Write( 199, getFlagDataFormat(spectrum.DataType));
            //TODO 200	1	byte	old_dc_count;
            //TODO 201	1	byte	old_ref_count;
            //TODO 202	1	byte	old_sample_count;
            //TODO 203	1	byte	application;
            B.Write( 204, (ushort)spectrum.NumberOfBands);//2	ushort	channels;
            //TODO 206	128	APP_DATA	app_data;
            //TODO 334	56	GPS_DATA	gps_data;
            //TODO 390	4	ulong	it;
            //TODO            B.Write(394, (Int16) spectrum.f	//2	int	fo;
            /*
            396	2	int	dcc;
            398	2	uint	calibration;
            400	2	uint	instrument_num;
            */
            if(spectrum.SensorCalibrationNumber != null) B.Write(398, (uint)spectrum.SensorCalibrationNumber);
            if(spectrum.SensorID != null) B.Write(400, (uint)spectrum.SensorID);
            
            var rangeBands = (from v in spectrum.Bands select v);
            var rangeBandValues = (from v in spectrum.BandValues select v);
            //B.Write(402, (spectrum.SpecType == SPECLIB_Spectrum.SpectrumType.RAW_TYPE) ? 0.0f : (float)range[2]); //* 402	4	float	ymin;
            //B.Write(406, (spectrum.SpecType == SPECLIB_Spectrum.SpectrumType.RAW_TYPE) ? 1.2f  : (float)range[3]); //	4	float	ymax;
            B.Write(402, (float)rangeBandValues.Min());  //402	4	float	ymin;
            B.Write(406, (float)rangeBandValues.Max());  //406	4	float	ymax;


            B.Write(410, (float)rangeBands.Min()); //410	4	float  	xmin;
            B.Write(414, (float)rangeBands.Max()); //414	4	float	xmax;
            
            //TEST 
            B.Write( 418, (uint)16); //418	2	uint	ip_numbits;
            B.Write( 420, (byte)1);//420	1	byte	xmode;
            /*            
            421	4	byte	flags[4];
            425	2	unsigned	dc_count;
            427	2	unsigned	ref_count;
            429	2	unsigned	sample_count;
                         * */
            B.Write( 431, getFlagASDInstrumentType(spectrum.IntrumentType)); //431	1	byte	instrument;

            /*
            432	4	ulong	bulb;
            436	2	uint	swir1_gain;
            438	2	uint	swir2_gain;
            440	2	uint	swir1_offset;   
            442	2	uint	swir2_offset;   
            444	4	float	splice1_wavelength;
            448	4	float	splice2_wavelength;
            452	12	char	when_in_ms[12];
            464	20	byte	spare[20];
            */

            if(dataType == typeof(Int32)) {
                B.WriteArray(484, Array.ConvertAll(spectrum.BandValues, item => (Int32)item));
            } else if(dataType == typeof(Single)) {
                B.WriteArray(484, Array.ConvertAll(spectrum.BandValues, item => (Single)item));
                size = sizeof(Single);
            } else {
                B.WriteArray(484, spectrum.BandValues);
            }
            


            return ba;
        }


        public static byte getFlagDataFormat(Type bandValuesDataType) {
            if(bandValuesDataType == typeof(Int16)) return 1;
            if(bandValuesDataType == typeof(Single)) return 0;
            if(bandValuesDataType == typeof(Double)) return 2;
            throw new Exception("Data type is not supported");
        }

        public static byte getFlagSpectrumType(Spectrum.SpectrumType t) {
                switch (t) {
                    case Spectrum.SpectrumType.RAW_TYPE: return 0;
                    case Spectrum.SpectrumType.REF_TYPE: return 1;
                    case Spectrum.SpectrumType.RAD_TYPE: return 2;
                    case Spectrum.SpectrumType.NOUNITS_TYPE: return 3;
                    case Spectrum.SpectrumType.IRRAD_TYPE: return 4;
                    case Spectrum.SpectrumType.QI_TYPE: return 5;
                    case Spectrum.SpectrumType.TRANS_TYPE: return 6;
                    case Spectrum.SpectrumType.UNKNOWN_TYPE: return 7;
                    case Spectrum.SpectrumType.ABS_TYPE: return 8;

                }
            return 7;
        }

        public static byte getFlagASDInstrumentType(Spectrum.InstrumentType t) {
            switch (t) {
                case Spectrum.InstrumentType.PSII: return 1;//"PSII"; break;
                case Spectrum.InstrumentType.LSVNIR: return 2;// InstrumentType.LSVNIR; //"LSVNIR"; break;
                case Spectrum.InstrumentType.FSVNIR: return 3; //"FSVNIR"; break;
                case Spectrum.InstrumentType.FSFR: return 4;   //"FSFR"; break;
                case Spectrum.InstrumentType.FSNIR: return 5;  //"FSNIR"; break;
                case Spectrum.InstrumentType.CHEMT: return 6;  //"CHEMT"; break;
                case Spectrum.InstrumentType.FSFR_UNATTENDED: return 7; //"FSFR_UNATTENDED_INSTRUMENT"; break;
                case Spectrum.InstrumentType.UNKNOWN: return 0;//"UNKNOWN_INSTRUMENT"; break;
                
            }
            return 0;
        }
    }
}
